package cn.jesin.css.config;

import cn.jesin.css.filter.CustomAuthenticationFilter;
import cn.jesin.css.filter.JwtAuthenticationTokenFilter;
import cn.jesin.css.filter.WebBaseFilter;
import cn.jesin.css.handler.EntryPointUnauthorizedHandler;
import cn.jesin.css.handler.MyAuthenticationFailureHandler;
import cn.jesin.css.handler.MyAuthenticationSuccessHandler;
import cn.jesin.css.handler.RestAccessDeniedHandler;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author 谷汉斌
 * @description SpringSecurity配置
 * @createTime 2024/7/24 上午10:03
 */
@EnableWebSecurity
@Configuration
public class WebSecurityConfig {

    @Resource
    private UserDetailsService userDetailsService;

    /**
     * 登录成功处理器
     */
    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    /**
     * 登录失败处理器
     */
    @Resource
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    @Resource
    private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;

    @Resource
    private RestAccessDeniedHandler restAccessDeniedHandler;

    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 密码解析器，注入容器
     * 为了数据安全性，在数据库中存放密码时不会存放 原密码，而是会存放加密后的密码。
     * 而用户传入的参数是明文密码。此时必须使用密码解析器才能将加密密码与明文密码做比对。
     *
     * @return BCryptPasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 认证管理器
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

    /**
     * 自定义登录过滤器
     */
    @Bean
    CustomAuthenticationFilter customAuthenticationFilter() {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setFilterProcessesUrl("/login");
        filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
        //设置统一的AuthenticationManager，必须要设置否则报错
        filter.setAuthenticationManager(authenticationManager());
        return filter;
    }

    /**
     * 配置过滤器链
     *
     * @param http http 请求
     * @return SecurityFilterChain 安全过滤器链
     * @throws Exception 异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // 登录处理器
        http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        /*
          在 UsernamePasswordAuthenticationFilter 之前添加 JwtAuthenticationTokenFilter
          用登录之后的认证信息，再次访问时可以直接访问了
         */
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                //初始处理请求和响应过滤器，在security过滤链之前拦截
                .addFilterBefore(new WebBaseFilter(), ChannelProcessingFilter.class);

        // 关闭csrf
        http.csrf(AbstractHttpConfigurer::disable);

        //配置拦截请求
        http.authorizeHttpRequests(auth -> auth
                // 放行指定路径下的所有请求
                .requestMatchers("/v3/api-docs/swagger-config","/v3/api-docs",
                        "/swagger-ui/*","register/pp","register/ep","/phone-code").permitAll()
                .requestMatchers("/admins/*").hasAuthority("admin")
                .requestMatchers("/users/*").hasAnyAuthority("user","admin")
                // 其他请求都需要认证
                .anyRequest().authenticated());

        //设置登录路径
        http.formLogin(f -> f
                .loginProcessingUrl("/login"));

        // 处理异常
        http.exceptionHandling(
                httpSecurityExceptionHandlingConfigurer -> httpSecurityExceptionHandlingConfigurer
                        .accessDeniedHandler(restAccessDeniedHandler)
                        .authenticationEntryPoint(entryPointUnauthorizedHandler)
        );

        //默认支持跨域请求
        return http.build();
    }
}